#include "ogreapplication.h"
#include <OgreErrorDialog.h>
#include <OgreKeyEvent.h>
#include <stdio.h>
#define ZOOM 100
#define SCALE 25
#define FACTOR 4
#define XCAMERASTART -100
#define YCAMERASTART 200
#define ZCAMERASTART 1000
#define DOUBLECLICKDELAY 500

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// methodes d'initilisation a mettre dans init()
//
// methodes actualisees a chaque raffraichissement -> dans frameEnded()
//
//
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Interface of MyApplication
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class MyApplication : public OgreApplication, Ogre::MouseListener
{
public:
    MyApplication();
    virtual void init();
    virtual bool frameStarted(const Ogre::FrameEvent& evt);
    virtual bool frameEnded(const Ogre::FrameEvent& evt);
    virtual void keyPressed(Ogre::KeyEvent* e);
    virtual void keyClicked(Ogre::KeyEvent* e);
    virtual void keyReleased(Ogre::KeyEvent* e);
    void ligne(Ogre::SceneNode* s ,Ogre::Vector3 pos1, Ogre::Vector3 pos2);
//ajout d'un element a la scene
    void attachEntity(Ogre::Entity * entity, Ogre::SceneNode * sceneNode, Ogre::Vector3 vScale, Ogre::Vector3 vTrans);

//corps vide {} -> non redefinies

    virtual void mousePressed(Ogre::MouseEvent*){};

    //methodes redefinies
    virtual void mouseReleased(Ogre::MouseEvent*);

    virtual void mouseClicked(Ogre::MouseEvent*){};

    virtual void mouseEntered(Ogre::MouseEvent*){};

    virtual void mouseExited(Ogre::MouseEvent*){};

private:
    Ogre::RaySceneQuery* mRaySceneQuery;
    Ogre::Entity*        mSelected;
    Ogre::Overlay*  mPanelOverlay;
    Ogre::String nomFic;
    Ogre::OverlayElement* fic;
    int nbLignes;
    Ogre::MaterialPtr ligneMaterial;
    Ogre::Timer* mButtonTimer;

};

using namespace Ogre;
using namespace std;

//************************************************************
// Implementation of MyApplication
//************************************************************
MyApplication::MyApplication()
        : OgreApplication("VISAGEFS"), mSelected(NULL)
{}

//redefinition de la methode mouseReleased MouseListener
void MyApplication::mouseReleased(Ogre::MouseEvent* e)
{
    //clic gauche
    if (e->getButtonID() == InputEvent::BUTTON0_MASK) 
    { 
        //double clic gauche        
     if (mButtonTimer->getMilliseconds()<DOUBLECLICKDELAY)
     {
        mCamera->setPosition(mSelected->getParentNode()->getPosition() + Vector3(XCAMERASTART,YCAMERASTART,ZCAMERASTART));
        mCamera->lookAt(mSelected->getParentNode()->getPosition());
        //fic->setCaption("double clik");
    
     }
     //}
     else
     //clic normal
     {
            // Deselect old one if any
            if (mSelected) mSelected->getParentSceneNode()->showBoundingBox(false);
            fic->setCaption("Nom du fichier: ");
            // Use a ray scene query to find where we clicked
            Ray mouseRay = mCamera->getCameraToViewportRay(e->getX(),e->getY());
            mRaySceneQuery->setRay(mouseRay);
            mRaySceneQuery->setSortByDistance(true);
            RaySceneQueryResult &result = mRaySceneQuery->execute();
            if (!result.empty())
            {
                RaySceneQueryResultEntry& re = result.front();
                if (re.movable && re.movable->getMovableType() == "Entity")
                {
                    mSelected = (Entity*)(re.movable);
                    mSelected->getParentSceneNode()->showBoundingBox(true);
                    fic->setCaption("Nom du fichier: " + mSelected->getName());
                }
            }
            //reset du timer
            mButtonTimer->reset();
        }
    }
}

//fonction pour tracer une ligne entre 2 objets
void MyApplication::ligne(SceneNode* sceneN,Vector3 pos1, Vector3 pos2) {
    ManualObject* myManualObject =  mSceneManager->createManualObject("Ligne" + nbLignes);
    SceneNode* myManualObjectNode = sceneN->createChildSceneNode("LigneNode" + nbLignes);


    myManualObject->begin("Color/Black" , Ogre::RenderOperation::OT_LINE_LIST);
    myManualObject->position(pos1);
    myManualObject->position(pos2);
    myManualObject->end();

    myManualObjectNode->attachObject(myManualObject);

    nbLignes++;
}

//fonction pour ajouter un objet a la scene
void MyApplication::attachEntity(Entity * entity, SceneNode * sceneNode, Vector3 vScale, Vector3 vTrans)
{
    sceneNode->scale(vScale);
    sceneNode->translate(vTrans);
    sceneNode->attachObject(entity);
}

//fonction d'initialisation de l'application
void MyApplication::init()
{
    //creation d'un timer pour gerer le double click de la souris
    mButtonTimer =  PlatformManager::getSingleton().createTimer();    mButtonTimer->reset();

    /*SceneNode * rootScene = mSceneManager->getRootSceneNode();*/
    nbLignes = 0;

    ligneMaterial = MaterialManager::getSingleton().create("ligneMaterial","debugger");
    ligneMaterial->setReceiveShadows(false);
    ligneMaterial->getTechnique(0)->setLightingEnabled(true);
    ligneMaterial->getTechnique(0)->getPass(0)->setDiffuse(0,0,1,0);
    ligneMaterial->getTechnique(0)->getPass(0)->setAmbient(0,0,1);
    ligneMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(0,0,1);


    mSceneManager->setAmbientLight(ColourValue(0.7f,0.7f,1.0f));

    //Configuration de la camera
    mCamera->setPosition(Vector3(XCAMERASTART,YCAMERASTART,ZCAMERASTART));
    mCamera->lookAt(Vector3(XCAMERASTART,YCAMERASTART,ZCAMERASTART-500));

    //Ajout de cubes
    Entity *cube1 = mSceneManager->createEntity("Racine","CubeRed.mesh");
    SceneNode* cube1Node = mSceneManager->getRootSceneNode()->createChildSceneNode("cube1Node");
    cube1Node->scale(Vector3(SCALE,SCALE,SCALE));
    cube1Node->attachObject(cube1);

    mSelected = mSceneManager->getEntity("Racine");

    //on recupere le rayon du premier cube
    /*Real r = mSceneManager->getEntity("Racine")->getBoundingRadius();*/

    Entity *cube2 = mSceneManager->createEntity("Rep2","CubeOrange.mesh");
    SceneNode* cube2Node = cube1Node->createChildSceneNode("cube2Node",(Vector3(-1,1,0)*FACTOR));
    cube2Node->attachObject(cube2);

    Entity *fichier1 = mSceneManager->createEntity("Fichier1","SphereYellow.mesh");
    SceneNode* fichier1Node = cube2Node->createChildSceneNode("fichier1Node",(Vector3(-1,1,-1)*FACTOR));
    fichier1Node->attachObject(fichier1);

    Entity *fichier2 = mSceneManager->createEntity("Fichier2","SphereGreen.mesh");
    SceneNode* fichier2Node = cube2Node->createChildSceneNode("fichier2Node",(Vector3(1,1,-1)*FACTOR));
    fichier2Node->attachObject(fichier2);

    Entity *fichier3 = mSceneManager->createEntity("Fichier3","SphereBlue.mesh");
    SceneNode* fichier3Node = cube2Node->createChildSceneNode("fichier3Node",(Vector3(0,1,1)*FACTOR));
    fichier3Node->attachObject(fichier3);

    Entity *fichier4 = mSceneManager->createEntity("Fichier4","SphereRed.mesh");
    SceneNode* fichier4Node = cube1Node->createChildSceneNode("fichier4Node",(Vector3(1,1,0)*FACTOR));
    fichier4Node->attachObject(fichier4);

    //Creation des liens entre les elements
    ligne(cube1Node,cube1Node->getPosition(),cube2Node->getPosition());
    ligne(cube2Node,cube1Node->getPosition(),fichier1Node->getPosition());
    ligne(cube2Node,cube1Node->getPosition(),fichier2Node->getPosition());
    ligne(cube2Node,cube1Node->getPosition(),fichier3Node->getPosition());
    ligne(cube1Node,cube1Node->getPosition(),fichier4Node->getPosition());
    // Adds a ground
    /*MeshManager::getSingleton().createPlane("plane.mesh","Custom",Plane(Vector3::UNIT_Z,Vector3(0.0,0.0,-r)),500*r,500*r,20,20);
    Entity* ground = mSceneManager->createEntity("Ground","plane.mesh");
    ground->setMaterialName("Examples/Rocky");
    SceneNode* groundNode = mSceneManager->getRootSceneNode()->createChildSceneNode();
    groundNode->attachObject(ground);
    groundNode->setOrientation(Quaternion(Radian(-Math::HALF_PI),Vector3::UNIT_X));*/

    // Activate shadows
    /*mSceneManager->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_MODULATIVE);
    mSceneManager->setShadowColour(Ogre::ColourValue(0.5f,0.5f,0.5f));
    cube1->setCastShadows(true);
    cube2->setCastShadows(true);
    cube3->setCastShadows(true);
    cube4->setCastShadows(true);*/
    mDebugPanelOverlay = OverlayManager::getSingleton().getByName("PanelOverlay");
    mDebugPanelOverlay->show();

    mSceneManager->setSkyBox(true,"Examples/StormySkyBox",50);
    mRaySceneQuery = mSceneManager->createRayQuery(Ray());

    fic = OverlayManager::getSingleton().getOverlayElement("Fichier");
    mEventProcessor->addMouseListener(this);

}

bool
MyApplication::frameStarted(const FrameEvent& evt)
{
    return OgreApplication::frameStarted(evt);
}

bool 
MyApplication::frameEnded(const FrameEvent& evt)
{

  mCamera->moveRelative(move);
  mSelected->getParentNode()->yaw(Degree(moveRootNode));
  return !mQuitRequested;
}


void MyApplication::keyPressed(KeyEvent *e)
{
    if (e->getKey() == KC_LEFT)
    {
        move = Vector3(-5.0,0.0,0.0);
        e->consume();
    }
    else if (e->getKey() == KC_RIGHT)
    {
        move = Vector3(5.0,0.0,0.0);
        e->consume();
    }
    else if (e->getKey() == KC_UP)
    {
        move = Vector3(0.0,5.0,0.0);
        e->consume();
    }
    else if (e->getKey() == KC_DOWN)
    {
        move = Vector3(0.0,-5.0,0.0);
        e->consume();
    }
    //zoom in
    else if (e->getKey() == KC_I)
    {
        move = Vector3(0.0,0.0,-5.0);
        e->consume();
    }
    //zoom out 
    else if (e->getKey() == KC_O)
    {
        move = Vector3(0.0,0.0,5.0);
        e->consume();
    }
    else if (e->getKey() == KC_O)
    {
        move = Vector3(0.0,0.0,5.0);
        e->consume();
    } 
    else if (e->getKey() == KC_Q)
    {
        moveRootNode=-2;
        e->consume();
    }
    else if (e->getKey() == KC_D)
    {
        //mCamera->yaw(Degree(1));
        moveRootNode=2;
        //mSceneManager->getRootSceneNode()->yaw(Degree(10));
        e->consume();
    }
    else if (e->getKey() == KC_SPACE)
    {
        mCamera->setPosition(Vector3(XCAMERASTART,YCAMERASTART,ZCAMERASTART));
        mCamera->lookAt(Vector3(XCAMERASTART,YCAMERASTART,ZCAMERASTART-500));
        e->consume();
    }
    /* pour pouvoir prendre une screenshot, plus tard     
    else if (e->getKey() == KC_SYSRQ)
    {
        RenderWindow::writeContentsToFile("screenshot.png");
        e->consume();
    }*/

}


void MyApplication::keyClicked(KeyEvent *e)
{
    if (e->getKey() == KC_ESCAPE)
    {
        requestQuit();
        e->consume();
    }
}


void MyApplication::keyReleased(KeyEvent *e)
{
    move = Vector3(0,0,0);
    moveRootNode = 0;
    e->consume();
}


int main(int, char **)
{
    MyApplication application;
    try
    {
        application.addResourceLocationFrom("resources.cfg");
        application.create("plugins.cfg","ogre.cfg","ogre.log",ST_GENERIC);
        application.run();
    }
    catch (Exception& e)
    {
        ErrorDialog* dlg = PlatformManager::getSingleton().createErrorDialog();
        dlg->display(e.getFullDescription());
        delete dlg;
        return 1;
    }
    return 0;
}


